#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "2d5p_aceMesh.h"
#include "task.h"


#define alpha_d 0.0876
#define beta_d  0.0765

#ifndef _LDM_STACK
#ifdef _MALLOC
#define A_rhs(i,j) A_slave_rhs[(i)*(s_endj-s_startj+2)+(j)]
#define A_lhs(i,j) A_slave_lhs[(i)*(s_endj-s_startj)+(j)]
__thread_local_fix double *A_slave_rhs;
__thread_local_fix double *A_slave_lhs;
#else
__thread_local_fix double A_rhs[BLKX+2][BLKY+2];
__thread_local_fix double A_lhs[BLKX][BLKY];
#endif
#endif
__thread_local volatile unsigned long get_reply,put_reply;
__thread_local_fix int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;

#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start1,dma_end1,dma_start2,dma_end2,dma_start3,dma_end3,dma_start4,dma_end4;
__thread_local volatile unsigned long comp_start1,comp_end1,comp_start2,comp_end2;
//__thread_local int core_id=0;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

extern double A[2][SIZEX][SIZEY];

void stencil_core(targs* args)
{

#ifdef _LDM_STACK
double A_rhs[BLKX+2][BLKY+2];
double A_lhs[BLKX][BLKY];
#endif
#ifdef _NAIIVE_LAM
/*0. assumptions  
  data region: [starti-1,endi] [startj-1,endj]   
  compute region: [starti, endi) [startj,endj) */
#ifdef _MALLOC
   int rhs_array_size;
   int lhs_array_size;
#endif
   int tsize, bsize, stride;
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
#ifdef _MALLOC
   rhs_array_size = (s_endi-s_starti+2)*(s_endj-s_startj+2);
   lhs_array_size = (s_endi-s_starti)*(s_endj-s_startj);
   A_slave_rhs = (double  *)ldm_malloc(rhs_array_size * sizeof(double));
   A_slave_lhs = (double  *)ldm_malloc(lhs_array_size * sizeof(double));
#endif
#ifdef _PROFILING
   dma_start1=rtc_();
#endif
#ifdef _MALLOC
// 1.fetch data
   get_reply=0;  
   bsize=(s_endj-s_startj+2)*8;
   stride=(SIZEY-(s_endj-s_startj+2))*8;  //stride: from blocktail to blockhead 
   tsize= bsize*(s_endi-s_starti+2);
   athread_get(PE_MODE,&A[src][s_starti-1][s_startj-1]
                 , &A_rhs(0,0)
                 , tsize,(void*) &get_reply
                 , 0, stride, bsize);
#else
   get_reply=0;  
   bsize=(s_endj-s_startj+2)*8;
   stride=(SIZEY-(s_endj-s_startj+2))*8;  //stride: from blocktail to blockhead 
   tsize= bsize*(s_endi-s_starti+2);
   athread_get(PE_MODE,&A[src][s_starti-1][s_startj-1]
                 , &A_rhs[0][0]
                 , tsize,(void*) &get_reply
                 , 0, stride, bsize);
#endif
//2.synchronize, wait for the transfer finish
   while(get_reply!=1);
#ifdef _PROFILING
   dma_end1=rtc_();
   slave_dma_count[_MYID]+=((dma_end1-dma_start1)); 
   comp_start1=rtc_();
#endif
//3.compute 
#ifdef _MALLOC
   for(i = 1; i < s_endi-s_starti+1; i++)
     for(j = 1; j < s_endj-s_startj+1; j++)
         A_lhs(i-1,j-1) = alpha_d * (A_rhs(i,j)) + \
                     beta_d * (A_rhs(i-1,j) + A_rhs(i+1,j) +\
                     A_rhs(i,j-1) + A_rhs(i,j+1));
#else
   for(i = 1; i < s_endi-s_starti+1; i++)
     for(j = 1; j < s_endj-s_startj+1; j++)
         A_lhs[i-1][j-1] = alpha_d * (A_rhs[i][j]) + \
                     beta_d * (A_rhs[i-1][j] + A_rhs[i+1][j] +\
                     A_rhs[i][j-1] + A_rhs[i][j+1]);
#endif
#ifdef _PROFILING
   comp_end1=rtc_();
   slave_comp_count[_MYID]+=(comp_end1-comp_start1);
   dma_start2=rtc_();
#endif
//4. write back
   put_reply=0;
   bsize=(s_endj-s_startj)*8;
   stride=(SIZEY-(s_endj-s_startj))*8;  //stride: from blocktail to blockhead
   tsize= bsize*(s_endi-s_starti);
#ifdef _MALLOC
   athread_put(PE_MODE,&A_lhs(0,0)
                , &A[dest][s_starti][s_startj]
                ,tsize,(void *)&put_reply,stride,bsize);
#else
   athread_put(PE_MODE,&A_lhs[0][0]
                , &A[dest][s_starti][s_startj]
                ,tsize,(void *)&put_reply,stride,bsize);    
#endif
//5.synchronize, wait for writing back finish.
   while(put_reply!=1); 
#ifdef _PROFILING
   dma_end2=rtc_();
   slave_dma_count[_MYID]+=((dma_end2-dma_start2));
#endif
#ifdef _MALLOC
   ldm_free(A_slave_rhs,rhs_array_size * sizeof(double));
   ldm_free(A_slave_lhs,lhs_array_size * sizeof(double));
#endif

#endif
#ifdef _MAIN_MEM
   int i,j,k;
   int  dest = (args->iter) % 2 ; 
   int  src = 1 - dest;
   for(i =  args->starti; i <  args->endi; i++)
      for(j =  args->startj; j <  args->endj; j++)
         A[dest][i][j] = alpha_d * A[src][i][j]+\
                         beta_d * (A[src][i-1][j] + A[src][i+1][j] +\
                         A[src][i][j-1] + A[src][i][j+1]);
#endif
}
